package framework.ai;

/**
 * The Reversi Module with build-in AI
 * @author Stephan Veenstra/Henderikus Harms
 * @version 0.1
 */
public class _ReversiAI extends AbstractAIModule {

	
	private final static int BOARD_WIDTH = 8;
	private final static int BOARD_HEIGHT = 8;
	private final static int DEPTH = 7;
	private final static String GAME_TYPE = "Reversi";
	
	public _ReversiAI() {
		super(BOARD_WIDTH,BOARD_HEIGHT, GAME_TYPE);
		this.initBoard();
	}
	
	public void doMove(int player, int move){
		
		int row = move/BOARD_HEIGHT;
		int col = move%BOARD_WIDTH;
		
		if(isValidMove(player, move)){
			this.board = this.place(this.board, move, player);
//			board[row][col] = player;
		}
	}
	
	@Override 
	public void initBoard() {
		// Set the start pieces
		board[4][3] = 1;
		board[3][4] = 1;
		board[3][3] = 2;
		board[4][4] = 2;
	};

	@Override
	public Integer getBestMove(int player) {
		BestMove move = chooseMove(player, board, 0);
		Integer best = null;
		best = (move.getRow() * board.length) + move.getCol();
		return best;
	}
	
	/**
	 * determine best move
	 * @param player
	 * @param gameBoard
	 * @return
	 */
	public BestMove chooseMove(int player, int[][] gameBoard, int t){
		int opp;
		BestMove reply;
		int simpleEval;
		int bestRow = 0;
		int bestCol = 0;
		int value = 0;
		int[][] gBoard = gameBoard;
		
		if((simpleEval = getState(player)) != UNKNOWN){
			System.err.println("state " + getState(player));
			return new BestMove(simpleEval);
		}
	
		if (player == P1){
			opp = P2;
			value = this.getCurrentScore(P1);
			System.err.println(this.getCurrentScore(P2) + " okaj");
		}else{
			opp = P1;
			value = this.getCurrentScore(P2);
			System.err.println(this.getCurrentScore(P1));
		}
		
			for(int i = 0; i < getWidth()*getHeight();i++)
			{
				if(isValidMove(player, i, gBoard)){
					System.err.println("check if working 00");
					if(t <= DEPTH){
						System.err.println("doesn't 0");
						gBoard = place(gBoard, i, player);
						reply = chooseMove(opp, gameBoard, ++t);
						System.err.println("replay " + reply.val);
						gBoard = place(gameBoard, i, EMPTY);
						System.err.println("doesn't 01");
						if(((player == P1) && (reply.val > value)) || ((player == P2) && (reply.val > value))){
							System.err.println("check if working 01");
							bestRow = i / BOARD_HEIGHT;
							bestCol = i % BOARD_WIDTH;	
							value = reply.val;
							
						}
					}
				}
					
			}
		
		return new BestMove(value, bestRow, bestCol);
	}
	
	public int[][] place(int[][] gameBoard, int move, int player) {
		
		int row = move / BOARD_HEIGHT;
		int col = move / BOARD_WIDTH;
		
		int[][] game = gameBoard;
		if(isValidMove(player, move, gameBoard)) {
			game[row][col] = player;
			game = this.flipAllPieces(player, move, gameBoard);
		}
		
		return game;
	}
	
	@Override
	public boolean isValidMove(int player, int move) {
		
		int row = move/BOARD_HEIGHT;
		int col = move%BOARD_WIDTH;
		
		if(player == this.getCurrentPlayer()){
			if((row >= 0 && row < BOARD_HEIGHT) && (col >=0 && col < BOARD_WIDTH)){
				if(board[row][col] == EMPTY){
					int points = getTotalPoints(player, move, board);
					System.out.println(points);
					if(points > 0) {
						return true;
					}
				}
			}
		}
		return false;
	}
	
	public boolean isValidMove(int player, int move, int[][]gameBoard){
		
		int row = move/BOARD_WIDTH;
		int col = move%BOARD_WIDTH;
		if(player == this.getCurrentPlayer()){
			if((row >= 0 && row < BOARD_HEIGHT) && (col >=0 && col < BOARD_WIDTH)){
				if(gameBoard[row][col] == EMPTY){
					if(getTotalPoints(player, move, gameBoard) > 0){
						return true;
					}
				}
			}
		}
		return false;
	}
	
	private int getPointsInDirection(int player, int startX, int startY, int offsetX, int offsetY,int[][] gameBoard){
		
		int x = startX+offsetX;
		int y = startY+offsetY;
		
		int points = 0;
		while(x < getWidth() && x >= 0 && y < getHeight() && y >= 0){
			if(gameBoard[x][y] == player){
				return points;
			}else if(gameBoard[x][y] == EMPTY){
				return 0;
			}else{
				points++;
				x+=offsetX;
				y+=offsetY;
			}
		}
		return 0;
	}
	
	/**
	 * flip pieces ai board
	 * @param player
	 * @param startX
	 * @param startY
	 * @param offsetX
	 * @param offsetY
	 * @param gameBoard
	 * @return
	 */
	private int[][] flipGameBoardPieces(int player, int startX, int startY, int offsetX, int offsetY,int[][] gameBoard){
		
		int x = startX+offsetX;
		int y = startY+offsetY;
		
		int[][] game = gameBoard;
		
		if(player == EMPTY){
			player = this.getOpponent();
		}
		
		while(x < getWidth() && x >= 0 && y < getHeight() && y >= 0){
			
			if(game[x][y] == this.getOpponent()){
				game[x][y] = player;
			}
			x+=offsetX;
			y+=offsetY;
		}
		return game;
	}
	
	/**
	 * Check the maximum amount of point the player can collect with this move
	 * @param player
	 * @param x
	 * @param y
	 * @return
	 */
	private int getTotalPoints(int player,int move, int[][]gameBoard){
		
		int row = move / BOARD_HEIGHT;
		int col = move % BOARD_WIDTH;
		int points = 0;
		points+= getPointsInDirection(player,row,col,-1,-1, gameBoard);		// NORTH-WEST
		points+= getPointsInDirection(player,row,col,0,-1, gameBoard);// NORTH
		points+= getPointsInDirection(player,row,col,1,-1, gameBoard);// NORTH-EAST
		points+= getPointsInDirection(player,row,col,1,0, gameBoard);// EAST
		points+= getPointsInDirection(player,row,col,1,1, gameBoard);		// SOUTH-EAST
		points+= getPointsInDirection(player,row,col,1,0, gameBoard);		// SOUTH
		points+= getPointsInDirection(player,row,col,0,1, gameBoard);		// SOUTH-WEST
		points+= getPointsInDirection(player,row,col,-1,1, gameBoard);		// WEST
		return points;
	}
	
	/**
	 * Check the maximum amount of point the player can collect with this move
	 * @param player
	 * @param x
	 * @param y
	 * @return
	 */
	private int[][] flipAllPieces(int player,int move, int[][]gameBoard){
		int x = move%BOARD_WIDTH;
		int y = move/BOARD_HEIGHT;
		int[][] b;
		b = flipGameBoardPieces(player,x,y,-1,-1, gameBoard); 	// NORTH-WEST
		b = flipGameBoardPieces(player,x,y,0,-1, gameBoard);		// NORTH
		b = flipGameBoardPieces(player,x,y,1,-1, gameBoard);		// NORTH-EAST
		b = flipGameBoardPieces(player,x,y,1,0, gameBoard);		// EAST
		b = flipGameBoardPieces(player,x,y,1,1, gameBoard);		// SOUTH-EAST
		b = flipGameBoardPieces(player,x,y,1,0, gameBoard);		// SOUTH
		b = flipGameBoardPieces(player,x,y,0,1, gameBoard);		// SOUTH-WEST
		b = flipGameBoardPieces(player,x,y,-1,1, gameBoard);		// WEST
		return b;
	}
	
	private int getCurrentScore(int player){
		int score = 0;
		for(int i = 0; i > getHeight()*getWidth(); i++){
			if(board[i%getWidth()][i/getWidth()] == player){
				score++;
			}
		}
		return score;
	}
	
	/**
	 * Check if both or one player still can make a move
	 * @return
	 */
	private boolean gameOver(){
		for(int i = 0; i > getHeight()*getWidth(); i++){
			if(board[i%getWidth()][i/getWidth()] == EMPTY){
				if(isValidMove(P1, i) || isValidMove(P2, i)){
					return false;
				}
			}
		}
		return true;
	}

	@Override
	protected int getState(int player) {
		if(gameOver()){
			int p1Score = getCurrentScore(P1);
			int p2score = getCurrentScore(P2);
			if(p1Score == p2score){
				return DRAW;
			}else if(p1Score > p2score){
				return player == P1 ? WIN : LOSE;
			}else{
				return player == P2 ? WIN : LOSE;
			}
		}
		return UNKNOWN;
	}
	
}
